package com.peter.aop.aopdemo.aop;

import com.alibaba.fastjson.JSON;
import com.peter.aop.aopdemo.dao.OpLog;
import com.peter.aop.aopdemo.logOperation.OperationLog;
import com.peter.aop.aopdemo.service.OperLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

//声明这是一个切面类
@Aspect
//此类交由Spring容器管理
@Component
public class OperLogAspect {
    private static final OpLog operLog = new OpLog();
    @Autowired
    private OperLogService operLogService;

    //设置操作日志切入点，记录操作日志， 在注解的位置切入代码
    @Pointcut("@annotation(com.peter.aop.aopdemo.logOperation.OperationLog)")
    public void operLogPointCut() {
    }

    @Around(value = "operLogPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Exception {
        // 1.方法执行前的处理，相当于前置通知
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 从切面织入点处通过反射机制获取织入点处的方法
        Map<String, Object> joinPointInfo = getJoinPointInfoMap(joinPoint);
        //获取RequestAttributes
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 创建一个日志对象(准备记录日志)


        Long startTime = System.currentTimeMillis();
        operLog.setOperCreateTime(LocalDateTime.now());//操作时间
        Object result = null;

        //让代理方法执行
        try {
            result = joinPoint.proceed();
            // 获取方法上面的注解
            OperationLog operationLog = method.getAnnotation(OperationLog.class);
            if (operationLog != null) {
                String operModul = operationLog.operModul();
                String operDesc = operationLog.operDesc();
                operLog.setOperModel(operModul);                // 操作模块
                operLog.setOperDesc(operDesc);                  // 操作描述
            }
            operLog.setOperRespParam(JSON.toJSONString(result));  //返回参数
            operLog.setOperUrl(request.getRequestURI());          //请求uri
            //完善请求信息
            Long returnTime = System.currentTimeMillis();
            operLog.setOperFinsishTime(returnTime - startTime);   //耗时时间

            if (StringUtils.isNotEmpty(joinPointInfo.get("paramMap").toString())) {
                operLog.setOperRequParam(joinPointInfo.get("paramMap").toString());//请求参数
                String operRequParam = operLog.getOperRequParam();
                if (StringUtils.isNotEmpty(operRequParam)) {
                    operLog.setOperUserName(operRequParam);
                    operLogService.insertLog(operLog);
                }
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    /**
     * 获得切入点方法信息
     *
     * @param * @param joinPoint
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @Author xiao lei
     * @Date 2020/12/18 11:22
     **/
    private static Map<String, Object> getJoinPointInfoMap(JoinPoint joinPoint) {
        Map<String, Object> joinPointInfo = new HashMap<>();
        String classPath = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        joinPointInfo.put("classPath", classPath);
        Class<?> clazz = null;
        CtMethod ctMethod = null;
        LocalVariableAttribute attr = null;
        int length = 0;
        int pos = 0;

        try {
            clazz = Class.forName(classPath);
            String clazzName = clazz.getName();
            ClassPool pool = ClassPool.getDefault();
            ClassClassPath classClassPath = new ClassClassPath(clazz);
            pool.insertClassPath(classClassPath);
            CtClass ctClass = pool.get(clazzName);
            ctMethod = ctClass.getDeclaredMethod(methodName);
            MethodInfo methodInfo = ctMethod.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr == null) {
                return joinPointInfo;
            }
            length = ctMethod.getParameterTypes().length;
            pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        Map<String, Object> paramMap = new HashMap<>();
        Object[] paramsArgsValues = joinPoint.getArgs();
        String[] paramsArgsNames = new String[length];
        for (int i = 0; i < length; i++) {
            paramsArgsNames[i] = attr.variableName(i + pos);
            String paramsArgsName = attr.variableName(i + pos);
            if (paramsArgsName.equalsIgnoreCase("request") ||
                    paramsArgsName.equalsIgnoreCase("response") ||
                    paramsArgsName.equalsIgnoreCase("session")
            ) {
                break;
            }
            Object paramsArgsValue = paramsArgsValues[i];
            paramMap.put(paramsArgsName, paramsArgsValue);
            joinPointInfo.put("paramMap", JSON.toJSONString(paramsArgsValue));
        }
        return joinPointInfo;
    }

    public static void updateTest(Integer id, Integer uid) {
        System.out.println(id);
        System.out.println(uid);
        operLog.setOperUserId(id);
    }
}

